/** \file test_extrudemesh.cpp \test
 *
 * Test Extrude Mesh
 *
 */

#include "meshkit/MKCore.hpp"
#include "meshkit/MeshOp.hpp"
#include "meshkit/ExtrudeMesh.hpp"
#include "meshkit/ModelEnt.hpp"

using namespace MeshKit;

#include "TestUtil.hpp"
#define DEFAULT_TEST_FILE "rect_quads.cub"

MKCore *mk;

void test_load_and_extrude();
void test_extrude_quad();
void test_extrude_tri();

void help_test_extrude_face(iMesh_EntityTopology topo, double *coords,
                            size_t nverts);

int main(int argc, char **argv)
{
  mk = new MKCore();
  int num_fail = 0;

  num_fail += RUN_TEST(test_load_and_extrude);
  num_fail += RUN_TEST(test_extrude_quad);
  num_fail += RUN_TEST(test_extrude_tri);

  delete mk;
  return num_fail;
}

void clear_mesh(MKCore *mk)
{
  iMesh *mesh = mk->imesh_instance();
  std::vector<iMesh::EntityHandle> ents;
  mesh->getEntities(mesh->getRootSet(), iBase_ALL_TYPES, iMesh_ALL_TOPOLOGIES,
                    ents);
  mesh->deleteEntArr(&ents[0], ents.size());
}

void test_load_and_extrude()
{
  clear_mesh(mk);
  std::string filename = TestDir + "/" + DEFAULT_TEST_FILE;
  mk->load_mesh(filename.c_str());

  // populate mesh with no attached geometry or relations
  mk->populate_model_ents(-1, 0, -1);

  // get the hexes
  MEntVector vols;
  mk->get_entities_by_dimension(2, vols);

  ExtrudeMesh *em = (ExtrudeMesh*) mk->construct_meshop("ExtrudeMesh", vols);
  em->set_name("extrude_mesh");

  // some entity tag types are always copy or expand
  em->expand_sets().add_tag("MATERIAL_SET");
  em->expand_sets().add_tag("DIRICHLET_SET");
  em->copy_sets().add_tag("NEUMANN_SET");
  em->copy_faces(true);

  Vector<3> dx; dx[0] = 0; dx[1] = 0; dx[2] = 3;
  em->set_transform(Extrude::Translate(dx, 1));

  // put them in the graph
  mk->get_graph().addArc(mk->root_node()->get_node(), em->get_node());
  mk->get_graph().addArc(em->get_node(), mk->leaf_node()->get_node());

  moab::Tag mattag;
  mk->moab_instance()->tag_get_handle( "MATERIAL_SET", 1, moab::MB_TYPE_INTEGER, mattag );
  moab::Range matsets;
  std::vector <moab::EntityHandle> set_ents;
  mk->moab_instance()->get_entities_by_type_and_tag( 0, moab::MBENTITYSET, &mattag, 0, 1, matsets );

  moab::Range::iterator set_it;
  for (set_it = matsets.begin(); set_it != matsets.end(); set_it++)  {
      moab::EntityHandle this_set = *set_it;
      em->extrude_sets().add_set((iMesh::EntitySetHandle)this_set);
    }

  // some entity tag types are always copy or expand
  em->extrude_sets().add_tag("MATERIAL_SET");

  // mesh embedded boundary mesh, by calling execute
  mk->setup_and_execute();

  // now delete all the 2D material sets:
  matsets.clear();
  // doing this again, to get new material sets created by extrude
  mk->moab_instance()->get_entities_by_type_and_tag( 0, moab::MBENTITYSET, &mattag, 0, 1, matsets );
  for (set_it = matsets.begin(); set_it != matsets.end(); set_it++)  {
      moab::EntityHandle this_set = *set_it;
      // get material sets with two dimension and delete
      std::vector <moab::EntityHandle> set_ents;
      mk->moab_instance()->get_entities_by_dimension(this_set, 2, set_ents, true);
      if(set_ents.size() > 0)
        mk->moab_instance()->delete_entities(&this_set, 1);
    }
  delete em;
}


void help_test_extrude_face(iMesh_EntityTopology topo, double *coords,
                            size_t nverts)
{
  clear_mesh(mk);

  iMesh *mesh = mk->imesh_instance();

  std::vector<iMesh::EntityHandle> verts(nverts);
  iMesh::EntityHandle face;
  iMesh::EntitySetHandle set;

  mesh->createVtxArr(nverts, iBase_INTERLEAVED, coords, &verts[0]);
  mesh->createEnt(topo, &verts[0], nverts, face);
  mesh->createEntSet(true, set);
  mesh->addEntToSet(face, set);

  ModelEnt me(mk, iBase_EntitySetHandle(0), /*igeom instance*/0,
              (moab::EntityHandle)set);
  MEntVector selection;
  selection.push_back(&me);

  ExtrudeMesh *em = (ExtrudeMesh*) mk->construct_meshop("ExtrudeMesh",
                                                        selection);
  em->set_name("extrude_mesh");

  Vector<3> dx; dx[0] = 0; dx[1] = 0; dx[2] = 10;
  em->set_transform(Extrude::Translate(dx, 10));
  em->copy_faces(true);

  // put them in the graph
  mk->get_graph().addArc(mk->root_node()->get_node(), em->get_node());
  mk->get_graph().addArc(em->get_node(), mk->leaf_node()->get_node());

  em->copy_sets().add_set(set);
  em->extrude_sets().add_set(set);

  // mesh embedded boundary mesh, by calling execute
  mk->setup_and_execute();

  std::vector<iMesh::EntityHandle> new_verts;
  std::vector<double> new_coords(nverts*3);
  std::vector<iMesh::EntityHandle> new_face;
  iMesh::EntitySetHandle copy_set;

  mesh->getEntSetEHData(set, em->copy_tag(), (iMesh::EntityHandle&)copy_set);

  mesh->getEntities(copy_set, iBase_ALL_TYPES, iMesh_ALL_TOPOLOGIES, new_face);
  CHECK_EQUAL(new_face.size(), size_t(1));

  mesh->getEntAdj(new_face[0], iBase_VERTEX, new_verts);
  CHECK_EQUAL(new_verts.size(), nverts);

  mesh->getVtxArrCoords(&new_verts[0], new_verts.size(), iBase_INTERLEAVED,
      &new_coords[0]);

  for(size_t i=0; i<nverts; i++) {
      CHECK_REAL_EQUAL(coords[i*3+0],    new_coords[i*3+0], 0.00001);
      CHECK_REAL_EQUAL(coords[i*3+1],    new_coords[i*3+1], 0.00001);
      CHECK_REAL_EQUAL(coords[i*3+2]+10, new_coords[i*3+2], 0.00001);
    }

  iMesh::EntitySetHandle extrude_set;
  int n;
  mesh->getEntSetEHData(set, em->extrude_tag(),
                        (iMesh::EntityHandle&)extrude_set);
  mesh->getNumOfType(extrude_set, iBase_ALL_TYPES, n);
  CHECK_EQUAL(n, 10);

  delete em;
}

void test_extrude_quad()
{
  double coords[] = {
    0, 0, 0,
    1, 0, 0,
    1, 1, 0,
    0, 1, 0
  };

  help_test_extrude_face(iMesh_QUADRILATERAL, coords, 4);
}

void test_extrude_tri()
{
  double coords[] = {
    0, 0, 0,
    1, 0, 0,
    1, 1, 0
  };

  help_test_extrude_face(iMesh_TRIANGLE, coords, 3);
}
